package c2

import (
	"errors"
	"fmt"
	"log"
	"reflect"
	"strings"
	"time"

	"gitee.com/tuboyou/c2/config"
	"gitee.com/tuboyou/c2/request"
	"gitee.com/tuboyou/c2/u2"
)

// Request 表示HTTP请求的结构体
type Request struct {
	Name        string                 `json:"name"`        // 请求名称
	Description string                 `json:"description"` // 请求描述
	Method      string                 `json:"method"`      // 请求方法（GET/POST等）
	Host        string                 `json:"host"`        // 请求主机
	Path        string                 `json:"path"`        // 请求路径
	Params      map[string]string      `json:"param"`       // 请求参数
	paramsDoc   []*ParamsDoc           // 参数文档
	Headers     map[string]string      `json:"headers"` // 请求头
	Body        string                 `json:"body"`    // 请求体（text/plain格式）
	bodyJson    interface{}            // 请求体（application/json格式）
	bodyDoc     []*ParamsDoc           // body参数文档
	bodyForm    map[string]string      // 请求体（application/x-www-form-urlencoded格式）
	bodyChoose  config.BodyChoose      // 请求体格式选择：0:none 1:text/plain 2:application/json 3:application/x-www-form-urlencoded 4:multipart/form-data
	Meta        map[string]string      `json:"meta"`    // 元数据，用于存储一些附加信息
	Timeout     int                    `json:"timeout"` // 超时时间（毫秒）
	expectBody  map[string]interface{} // 期望返回的body结构
	Error       error                  `json:"error"`             // 错误信息
	Retry       int                    `json:"retry" default:"0"` // 重试次数
}

// New 创建一个新的HTTP请求
//
// 参数：
//
//	name - 请求名称
//	method - 请求方法（GET/POST等）
//	host - 请求主机
//	path - 请求路径
//
// 返回值：
//
//	*Request - 新创建的Request对象
//
// 注意：
//
//  1. 默认会添加xfrom请求头
//  2. 默认超时时间为5000毫秒
//
// 示例：
//
//	req := New("test", "GET", "https://example.com", "/api/v1/test")
func New(name, method, host, path string) *Request {
	if iconf.showDebug {
		log.Println("新建一个", method, "请求：", name, host+path)
	}
	return &Request{Name: name, Method: method, Host: host, Path: path, Params: make(map[string]string), Headers: map[string]string{"xfrom": iconf.xfrom}, bodyForm: make(map[string]string), Meta: make(map[string]string), bodyChoose: 0, Timeout: 5000, expectBody: make(map[string]interface{}), bodyDoc: make([]*ParamsDoc, 0), bodyJson: nil, paramsDoc: make([]*ParamsDoc, 0)}
}

// Get 创建一个新的GET请求
//
// 参数：
//
//	name - 请求名称
//	host - 请求主机
//	path - 请求路径
//
// 返回值：
//
//	*Request - 新创建的Request对象
//
// 注意：
//
//  1. 默认会添加xfrom请求头
//  2. 默认超时时间为5000毫秒
func Get(name, host, path string) *Request {
	return New(name, "GET", host, path)
}

func GetOrPostByConfig(name, host, path string) *Request {
	if iconf.getOrPost {
		return Post(name, host, path)
	} else {
		return Get(name, host, path)
	}
}

// Post 创建一个新的POST请求
//
// 参数：
//
//	name - 请求名称
//	host - 请求主机
//	path - 请求路径
//
// 返回值：
//
//	*Request - 新创建的Request对象
//
// 注意：
//
//  1. 默认会添加xfrom请求头
//  2. 默认超时时间为5000毫秒
//  3. 不包含multipart/form-data格式
func Post(name, host, path string) *Request {
	return New(name, "POST", host, path)
}

// SetRetry 设置全局重试次数
//
// 参数：
//
//	r - 重试次数，最大10次
//
// 注意：
//
//  1. 如果r小于0，则设置为0
//  2. 如果r大于10，则设置为10
func SetRetry(r int) {
	if r < 0 {
		r = 0
	}
	if r > 10 {
		r = 10
	}
	iconf.retry = r
	if iconf.showDebug {
		log.Println("设置重试次数：", r)
	}
}

// SetTimeout 设置请求超时时间
//
// 参数：
//
//	timeout - 超时时间，单位为毫秒
//
// 注意：
//
//	超时时间会应用到整个请求过程
func (t *Request) SetTimeout(timeout int) {
	t.Timeout = timeout
	if iconf.showDebug {
		log.Println("设置超时时间（毫秒）：", timeout)
	}
}

func (t *Request) GetBodyJson() interface{} {
	return t.bodyJson
}

func (t *Request) GetBodyJsonParamString(key string) string {
	if t.bodyJson == nil {
		return ""
	}
	if m, ok := t.bodyJson.(map[string]interface{}); ok {
		if v, ok := m[key]; ok {
			return u2.I2S(v)
		}
	}
	return ""
}

// SetExpectBody 设置期望的响应体结构
//
// 参数：
//
//	name - 期望结构的名称
//	value - 期望结构的值
//
// 注意：
//
//  1. 如果value为nil，则设置为空字符串
//  2. 如果value为指针类型，则直接存储指针
func (t *Request) SetExpectBody(name string, value interface{}) {
	if t.expectBody == nil {
		t.expectBody = make(map[string]interface{})
	}
	if value == nil {
		log.Println("expectBody value is nil")
		t.expectBody[name] = ""
		return
	}
	if reflect.TypeOf(value).Kind() == reflect.Ptr {
		t.expectBody[name] = value
		return

	}
	t.expectBody[name] = &value

}

// Curl 生成并返回当前请求的curl命令
//
// 返回值：
//
//	string - 生成的curl命令
//
// 注意：
//
//  1. 会自动包含请求方法、URL、请求头和请求体
//  2. 对于POST请求，会自动添加-d参数
//  3. 对于multipart/form-data请求，会自动添加-F参数
func (t *Request) Curl() string {
	// 构建基本curl命令
	command := fmt.Sprintf("curl -X %s '%s'", t.Method, t.Host+t.Path)

	// 添加请求参数
	if len(t.Params) > 0 {
		var params []string
		for k, v := range t.Params {
			params = append(params, fmt.Sprintf("%s=%s", k, v))
		}
		command += "?" + strings.Join(params, "&")
	}

	// 添加请求头
	for k, v := range t.Headers {
		command += fmt.Sprintf(" -H '%s: %s'", k, v)
	}

	// 处理请求体
	t.Body2String()
	if t.Body != "" {
		switch t.bodyChoose {
		case config.BodyForm:
			// 处理application/x-www-form-urlencoded格式
			command += fmt.Sprintf(" -d '%s'", t.Body)
		case config.BodyJson:
			// 处理application/json格式
			command += fmt.Sprintf(" -d '%s'", t.Body)
		case config.BodyString:
			// 处理text/plain格式
			command += fmt.Sprintf(" -d '%s'", t.Body)
		}
	}

	return command
}

// SetDescription 设置请求描述
//
// 参数：
//
//	description - 描述内容
//
// 返回值：
//
//	*Request - 返回当前Request对象，支持链式调用
func (t *Request) SetDescription(description string) *Request {
	t.Description = description
	return t
}

// SetParam 设置请求参数
//
// 参数：
//
//	Name - 参数名称
//	Value - 参数值
//
// 注意：
//
//  1. 如果Value为nil，则删除该参数
//  2. 如果Value为time.Time类型，会自动格式化为"2006-01-02 15:04:05"
func (t *Request) SetParam(Name string, Value interface{}) *ParamsDoc {
	pd := NewParamsDoc(Name)
	t.paramsDoc = append(t.paramsDoc, pd)
	if Value == nil {
		delete(t.Params, Name)
		if iconf.showDebug {
			log.Println("删除参数：", Name)
		}

		return pd
	}
	ti, ok := Value.(time.Time)
	if ok {
		t.Params[Name] = ti.Format("2006-01-02 15:04:05")
		if iconf.showDebug {
			log.Println("设置参数：", Name, ",值：", t.Params[Name])
		}
		return pd
	}
	t.Params[Name] = u2.I2S(Value)
	if iconf.showDebug {
		log.Println("设置参数：", Name, ",值：", t.Params[Name])
	}
	return pd
}

// GetBodyType 获取请求体格式类型
//
// 返回值：
//
//	int - 请求体格式类型代码
//
// 注意：
//
//	0:none 1:text/plain 2:application/json 3:application/x-www-form-urlencoded 4:multipart/form-data
func (t *Request) GetBodyType() config.BodyChoose {
	return t.bodyChoose
}

// SetHeader 设置请求头
//
// 参数：
//
//	Name - 头名称
//	Value - 头值
func (t *Request) SetHeader(Name, Value string) {
	t.Headers[Name] = Value
	if iconf.showDebug {
		log.Println("设置header：", Name, ",值：", Value)
	}
}

// SetMeta 设置元数据
//
// 参数：
//
//	Name - 元数据名称
//	Value - 元数据值
func (t *Request) SetMeta(Name, Value string) {
	t.Meta[Name] = Value
	if iconf.showDebug {
		log.Println("设置meta：", Name, ",值：", Value)
	}
}

// SetBody 设置请求体
//
// 参数：
//
//	body - 请求体内容
//
// 注意：
//
//  1. 会覆盖原有的body
//  2. 请求头的content-type需要自行设置
func (t *Request) SetBody(body string) *ParamsDoc {
	t.Body = body
	t.bodyChoose = config.BodyString
	if iconf.showDebug {
		log.Println("设置body：", body)
	}
	pd := NewParamsDoc("body")
	t.bodyDoc = append(t.bodyDoc, pd)
	return pd
}

// SetBodyJson 设置JSON格式的请求体
//
// 参数：
//
//	body - JSON格式的请求体
//
// 注意：
//
//  1. 会覆盖原有的body
//  2. 会自动设置请求头的content-type为application/json
func (t *Request) SetBodyJson(body interface{}) *ParamsDoc {
	t.bodyJson = body
	t.bodyChoose = config.BodyJson
	if iconf.showDebug {
		log.Println("设置body：", u2.I2S(body), "设置请求头：Content-Type:application/json")
	}
	pd := NewParamsDoc("body")
	t.bodyDoc = append(t.bodyDoc, pd)
	return pd
}

// SetBodyJsonMap 分步设置JSON格式的请求体（map类型）
//
// 参数：
//
//	key - JSON字段名
//	value - JSON字段值
//
// 注意：
//
//  1. 如果body不是map类型，会返回错误
//  2. 会自动设置请求头的content-type为application/json
func (t *Request) SetBodyJsonMap(key string, value interface{}) *ParamsDoc {
	pd := NewParamsDoc(key)
	t.bodyDoc = append(t.bodyDoc, pd)
	if t.Error != nil {
		return pd
	}
	if t.bodyJson == nil {
		t.bodyJson = make(map[string]interface{})
	}

	if u2.I2S(value) == "null" {
		delete(t.bodyJson.(map[string]interface{}), key)
		if iconf.showDebug {
			log.Println("输入值为null，移除key:", key)
		}
		return pd
	}
	if v, ok := t.bodyJson.(map[string]interface{}); ok {
		v[key] = value
		t.bodyChoose = config.BodyJson
	} else {
		t.Error = errors.New("bodyJson is not map，key:" + key)
		if iconf.showDebug {
			log.Println("设置出现异常：bodyJson is not map，key:", key)
		}
	}
	if iconf.showDebug {
		log.Println("设置body："+key, "值：", u2.I2S(value), "，设置请求头：Content-Type:application/json")
	}
	return pd
}

// SetBodyJsonList 分步设置JSON格式的请求体（list类型）
//
// 参数：
//
//	value - 要添加的列表项
//
// 注意：
//
//  1. 如果body不是list类型，会返回错误
//  2. 会自动设置请求头的content-type为application/json
func (t *Request) SetBodyJsonList(value interface{}) *ParamsDoc {
	pd := NewParamsDoc("body")
	t.bodyDoc = append(t.bodyDoc, pd)

	if t.Error != nil {
		return pd
	}
	if t.bodyJson == nil {
		t.bodyJson = make([]interface{}, 0)
	}
	if v, ok := t.bodyJson.([]interface{}); ok {
		t.bodyJson = append(v, value)
		t.bodyChoose = config.BodyJson
	} else {
		t.Error = errors.New("bodyJson is not list")
		if iconf.showDebug {
			log.Println("设置出现异常：bodyJson is not list")
		}
	}
	if iconf.showDebug {
		log.Println("设置body：", u2.I2S(t.bodyJson), "，设置请求头：Content-Type:application/json")
	}
	return pd
}

// SetBodyForms 设置application/x-www-form-urlencoded格式的请求体
//
// 参数：
//
//	body - 键值对形式的请求体
//
// 注意：
//
//  1. 会覆盖原有的body
//  2. 会自动设置请求头的content-type为application/x-www-form-urlencoded
func (t *Request) SetBodyForms(body map[string]string) *ParamsDoc {
	t.bodyForm = body
	t.bodyChoose = config.BodyForm
	if iconf.showDebug {
		log.Println("设置body：", u2.I2S(body), "设置请求头：Content-Type:application/x-www-form-urlencoded")
	}
	pd := NewParamsDoc("body")
	t.bodyDoc = append(t.bodyDoc, pd)
	return pd
}

// GetbodyForms 获取application/x-www-form-urlencoded格式的请求体
//
// 返回值：
//
//	map[string]string - 键值对形式的请求体
func (t *Request) GetbodyForms() map[string]string {

	return t.bodyForm
}

// SetBodyForm 分步设置application/x-www-form-urlencoded格式的请求体
//
// 参数：
//
//	Name - 字段名
//	Value - 字段值
//
// 注意：
//
//  1. 如果Value为nil，则删除该字段
//  2. 如果Value为time.Time类型，会自动格式化为"2006-01-02 15:04:05"
func (t *Request) SetBodyForm(Name string, Value interface{}) *ParamsDoc {
	pd := NewParamsDoc(Name)
	t.bodyDoc = append(t.bodyDoc, pd)
	t.bodyChoose = config.BodyForm
	if Value == nil {
		delete(t.bodyForm, Name)
		if iconf.showDebug {
			log.Println("删除body：", Name)
		}
		return pd
	}
	if ti, ok := Value.(time.Time); ok {
		t.bodyForm[Name] = ti.Format("2006-01-02 15:04:05")
		if iconf.showDebug {
			log.Println("设置body：", Name, ",值：", t.bodyForm[Name])
		}
		return pd
	}
	t.bodyForm[Name] = u2.I2S(Value)
	if iconf.showDebug {
		log.Println("设置body：", Name, ",值：", t.bodyForm[Name])
	}
	return pd
}

// String 获取请求的字符串表示
//
// 返回值：
//
//	string - 请求的JSON格式字符串
func (t *Request) String() string {
	da, _ := u2.Struct2JSON(t, true)
	return da
}

// Body2String 将请求体转换为字符串
//
// 返回值：
//
//	string - 请求体字符串
//
// 注意：
//
//  1. 会根据bodyChoose自动转换请求体格式
//  2. 会自动设置对应的Content-Type
func (t *Request) Body2String() string {
	if t.Body != "" {
		return t.Body
	}
	if t.bodyChoose == config.BodyForm {
		var builder strings.Builder
		// t.Body = ""
		for k, v := range t.bodyForm {
			builder.WriteString(k)
			builder.WriteString("=")
			builder.WriteString(v)
			builder.WriteString("&")
			// t.Body += k + "=" + v + "&"
		}
		t.Body = strings.TrimRight(builder.String(), "&")
		t.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	} else if t.bodyChoose == config.BodyJson {
		t.Body = ""
		body, e := u2.Struct2JSON(t.bodyJson, false)
		if e == nil {
			t.Body = body
		} else {
			t.Error = e
		}
		t.SetHeader("Content-Type", "application/json")
	} else if t.bodyChoose != config.BodyString {
		t.Body = ""
	}

	return t.Body
}

// GetBodyVal 获取application/x-www-form-urlencoded格式请求体的指定字段值
//
// 参数：
//
//	key - 字段名
//
// 返回值：
//
//	string - 字段值
//
// 注意：
//
//	如果请求体不是application/x-www-form-urlencoded格式，返回空字符串
func (t *Request) GetBodyVal(key string) string {
	if t.bodyChoose != config.BodyForm {
		return ""
	}
	if v, ok := t.bodyForm[key]; !ok {
		return ""
	} else {
		return v
	}
}

// SetRetry 设置请求重试次数
//
// 参数：
//
//	r - 重试次数
func (t *Request) SetRetry(r int) {
	t.Retry = r
}

// Run 执行HTTP请求
//
// 返回值：
//
//	Response - 包含响应结果的结构体
//
// 注意：
//
//  1. 会自动处理重试逻辑
//  2. 会将结果保存到指定路径（如果设置了结果保存路径）
//  3. 会合并全局请求头
func (t *Request) Run() Response {

	query := make(map[string]string)
	for k, v := range t.Params {
		query[k] = v
	}
	if iconf.getOrPost {
		switch iconf.getOrPostBodyPosition {
		case 1:
			t.bodyForm = t.Params
			t.bodyChoose = config.BodyForm
			query = make(map[string]string)
		case 2:
			bodyq := make(map[string]string)
			for k, v := range t.Params {
				bodyq[k] = v
			}
			t.bodyJson = bodyq
			t.bodyChoose = config.BodyJson
			query = make(map[string]string)
		}
	}
	t.Body2String()
	tmpHeaders := make(map[string]string)
	for k, v := range iconf.globalHeaders {
		tmpHeaders[k] = v
	}

	for k, v := range t.Headers {
		tmpHeaders[k] = v
	}

	req := request.NewRequest(t.Name, t.Method, t.Host+t.Path, query, t.Body, tmpHeaders, t.Timeout, t.Error)
	req.SetExpectBody(t.expectBody)
	inner_retry := t.Retry
	if inner_retry == 0 {
		inner_retry = iconf.retry
	}
	var res Response

	for i := -1; i < inner_retry; i++ {
		inner_res := req.Run()
		res = Response(inner_res)
		res.Result2File(iconf.resultPath, true, false)
		if res.Error == nil || req.GetError() != nil {
			break
		} else {
			if iconf.showDebug {
				if i == -1 {
					log.Print("请求异常：", req.Url.String(), ",错误信息：\n", req.GetError())
					if inner_retry > 0 {
						log.Println(",正在重试")
					}
				} else {
					log.Println("请求异常[重试]：", req.Url.String(), ",错误信息：\n", res.Error, ",重试：", i+1, "/", inner_retry)
				}
			}
		}
	}
	return res
}
